package fr.labri.harmony.analysis.jsep.results.filter;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import fr.labri.harmony.analysis.jsep.results.Counter;
import fr.labri.harmony.analysis.jsep.results.DBConnection;
import fr.labri.harmony.analysis.jsep.results.LoadRules;

public class MigrationUsageLibrary extends ResultFilter {

	@Override
	public void computeResult(ResultSet rs) throws SQLException, IOException {
		ResultSet rsView = DBConnection.fetchDataView();
		Map<String, Long> end_projects = new HashMap<String, Long>();
		rs.first();
		Map<String,Map<Long,Set<String>>> add_clients = new HashMap<String, Map<Long,Set<String>>>();
		Map<String,Map<Long,Set<String>>> rem_clients = new HashMap<String, Map<Long,Set<String>>>();
		System.out.println("Start Browsing revisions");
		int cpt=0;

		while (rs.next()){	
			cpt++;
			if(cpt%10000==0)
				System.out.println(cpt);
			String project = rs.getString("project");
			//Timestamp at
			if(rs.getString("TIMESTAMP_AT") != null ) {
				//for the added lib
				String lib = rs.getString("NAME");
				long datemig = Long.parseLong(rs.getString("TIMESTAMP_AT"))*1000;
				if(!add_clients.containsKey(lib)) add_clients.put(lib, new HashMap<Long, Set<String>>());
				if(!add_clients.get(lib).containsKey(datemig)) add_clients.get(lib).put(datemig, new HashSet<String>());
				add_clients.get(lib).get(datemig).add(project);
			}
			//Timestamp at
			if(rs.getString("LIBS_SET_AT") != null ) {
				String libs_used = rs.getString("LIBS_SET_AT").replaceAll(" ", "");
				Set<String> libs =  Utils.filterLibs(libs_used.replaceAll("\\[", "").replaceAll("\\]", "").split("\\,"));
				long datemig = Long.parseLong(rs.getString("TIMESTAMP_AT"))*1000;
				for(String lib : libs) {
					if(!add_clients.containsKey(lib)) add_clients.put(lib, new HashMap<Long, Set<String>>());
					if(!add_clients.get(lib).containsKey(datemig)) add_clients.get(lib).put(datemig, new HashSet<String>());
					add_clients.get(lib).get(datemig).add(project);
				}
			}
			//Start migration
			if(rs.getString("MIGRATION_LIBS_SET_IN") != null ) {
				String libs_used = rs.getString("MIGRATION_LIBS_SET_IN").replaceAll(" ", "");
				Set<String> libs =  Utils.filterLibs(libs_used.replaceAll("\\[", "").replaceAll("\\]", "").split("\\,"));
				long datemig = Long.parseLong(rs.getString("MIGRATION_TIMESTAMP_IN"))*1000;
				for(String lib : libs) {
					if(!add_clients.containsKey(lib)) add_clients.put(lib, new HashMap<Long, Set<String>>());
					if(!add_clients.get(lib).containsKey(datemig)) add_clients.get(lib).put(datemig, new HashSet<String>());
					add_clients.get(lib).get(datemig).add(project);
				}
			}
			//at the end of the migration segment
			if(rs.getString("LIBS_SET_OUT") != null ) {
				String libs_used = rs.getString("LIBS_SET_OUT").replaceAll(" ", "");
				Set<String> libs =  Utils.filterLibs(libs_used.replaceAll("\\[", "").replaceAll("\\]", "").split("\\,"));
				long datemig = Long.parseLong(rs.getString("TIMESTAMP_OUT"))*1000;
				for(String lib : libs) {
					if(!add_clients.containsKey(lib)) add_clients.put(lib, new HashMap<Long, Set<String>>());
					if(!add_clients.get(lib).containsKey(datemig)) add_clients.get(lib).put(datemig, new HashSet<String>());
					add_clients.get(lib).get(datemig).add(project);
				}
			}
			//Removed lib
			if(rs.getString("TIMESTAMP_OUT") != null && rs.getString("MIGRATION_LIB_TO")!=null) {	
				String lib = rs.getString("NAME");
				long datemig = Long.parseLong(rs.getString("TIMESTAMP_OUT"))*1000;
				if(!rem_clients.containsKey(lib)) rem_clients.put(lib, new HashMap<Long, Set<String>>());
				if(!rem_clients.get(lib).containsKey(datemig)) rem_clients.get(lib).put(datemig, new HashSet<String>());
				rem_clients.get(lib).get(datemig).add(project);
			}
			//Last out
			if(rs.getString("LAST_LIBS") != null && !rs.getString("LAST_LIBS").equals("null")) {
				String libs_used = rs.getString("LAST_LIBS").replaceAll(" ", "");
				Set<String> libs =  Utils.filterLibs(libs_used.replaceAll("\\[", "").replaceAll("\\]", "").split("\\,"));

				long datemig = 0L;
				if(end_projects.containsKey(project))
					datemig = end_projects.get(project);
				else {
					rsView.first();
					while (rsView.next()){	
						String url = rsView.getString("url");
						if(url.equals(project)) {
							datemig = rsView.getLong("END")*1000;
							end_projects.put(project, datemig);
							break;
						}
					}
				}
				for(String lib : libs) {
					if(!add_clients.containsKey(lib)) add_clients.put(lib, new HashMap<Long, Set<String>>());
					if(!add_clients.get(lib).containsKey(datemig)) add_clients.get(lib).put(datemig, new HashSet<String>());
					add_clients.get(lib).get(datemig).add(project);
				}
			}
		}
		System.out.println("Compute score for each library");
		//Compute score for each library
		Map<String,Map<Long,Integer>> usage_libs = new HashMap<String, Map<Long,Integer>>();
		//Only for libraries that migrate
		//for(String lib : LoadRules.getLibraries()) {
		//For all libraries
		for(String lib : add_clients.keySet()) {
			usage_libs.put(lib, new HashMap<Long, Integer>());
			if(add_clients.get(lib)!=null) {
				List<Long> times = new ArrayList<Long>(add_clients.get(lib).keySet());
				if(rem_clients.containsKey(lib))
					times.addAll(rem_clients.get(lib).keySet());
				Collections.sort(times);
				Set<String> clients = new HashSet<String>();
				for(Long ts : times) {
					if(add_clients.containsKey(lib))
						if(add_clients.get(lib).containsKey(ts))
							clients.addAll(add_clients.get(lib).get(ts));
					if(rem_clients.containsKey(lib))
						if(rem_clients.get(lib).containsKey(ts))
							clients.removeAll(rem_clients.get(lib).get(ts));
					usage_libs.get(lib).put(ts, clients.size());
				}
			}
		}

		System.out.println("Compute score for each category");
		//Compute usage for each category
		for(Set<String> libs : LoadRules.getCategories()) {
			List<Long> times = new ArrayList<Long>();
			for(String lib : libs) {
				times.addAll(usage_libs.get(lib).keySet());
			}
			Collections.sort(times);
			for(String lib : libs) {
				addIfMissing(times, usage_libs.get(lib));
			}
			PrintStream ps = new PrintStream("usage_"+libs.toString().substring(0,10)+"_format.csv");
			String header = "TIME";
			for(String lib : libs) {
				header+=";"+lib;
			}
			ps.println(header);
			for(long time : times) {
				//ps.print(time);
				ps.print(new SimpleDateFormat("yyyy-MM-dd").format(new Date(time)));
				for(String lib : libs) {
					ps.print(";"+usage_libs.get(lib).get(time));
				}
				ps.print("\n");
			}
			ps.close();
		}
		System.out.println("Libraries individual usage : "+usage_libs.keySet().size()+ " libraries");
		//Usage per apis
		PrintStream ps = new PrintStream(new FileOutputStream("libraries_usage.csv"));
		ps.println("LIBRARY , USAGE , IN-DEGREE, OUT-DEGREE");
		for(String library : usage_libs.keySet()) {
			List<Long> times = new ArrayList<Long>(usage_libs.get(library).keySet());
			Collections.sort(times);
			ps.println(library+","+usage_libs.get(library).get(times.get(times.size()-1))+","+LoadRules.getInDegree(library)+","+LoadRules.getOutDegree(library));
		}
		ps.close();
	}

	private void addIfMissing(List<Long> times, Map<Long, Integer> map) {
		List<Long> ts = new ArrayList<Long>(map.keySet());
		Collections.sort(ts);
		for(long time : times) {
			if(!map.containsKey(time)) {
				if(ts.get(0) > time) 
					map.put(time, 0);
				int index = -1;
				for(int k = 0 ; k < ts.size() ; k++) {
					if(ts.get(k) < time )
						index = k;
				}
				if(index!=-1) {
					map.put(time, map.get(ts.get(index)));
				}
			}
		}
	}
}
